3.0 TypeScript 问题答疑及经验分享 您所在的位置:网站首页 ts 感叹号 3.0 TypeScript 问题答疑及经验分享

3.0 TypeScript 问题答疑及经验分享

2024-01-20 15:39| 来源: 网络整理| 查看: 265

虽然 Creator 很早就支持了 TypeScript,直到 3.0 我们才正式废弃了对 JavaScript 的支持。但随着这次 3.0 的全面升级,很多第一次使用 TypeScript 的开发者仍然遇到了不少阻碍。

为了让大家的升级更加平滑,本文将对这次调整做一次简要回顾,并且解答开发者在语言升级的过程中容易遇到的一些问题。

我们在 TS 上的铺垫

我们对 TypeScript 的支持由来已久,最早可追溯到 17 年 5 月发布的 v1.5.0,此后我们一直在听取 TS 开发者的反馈,逐步完善 TS 的支持。

在 18 年的时候,3D 项目组开始用 TS 重写整个 3D 引擎和编辑器,验证了 TS 处理超大规模项目时的能力,让我们对 TS 有了更大的信心和第一手的使用经验。

到了 19 年,我们在 Cocos Creator 3D 正式发布时,第一次正式废弃了对 JavaScript 的支持,而后持续听取开发者们对这一改变的反馈。

要采用哪一门语言做为引擎的开发语言,不是一个能轻易做出的决定,对开发者、对生态都影响巨大。 Cocos Creator 当初选择 JS 时,已经经历过一次饱受非议,这次如果不是有前面几年在 TS 上的铺垫,我们也没有勇气在 3.0 做出这个关键调整。

为什么要转向 TS

在 Cocos Creator 14 年刚立项之初,由于熟悉的语法,TypeScript 成为了我们入门 JavaScript 的良好桥梁。但由于当时的 TS 刚刚正式发布,生态仍未成熟,因此我们只能先使用最原生的 JavaScript。

经过了这么多年的发展,今天 TS 已经成为一门非常成熟的语言,有丰富的生态和群众基础,可以胜任任何类型的游戏开发需要!

从我这几年跟研发团队的接触来看,大多数团队都逃不过 TS 的真香定律,网上也有大量对 TS 的安利文章,那么我这边就不赘述 TS 的优势。我分享下从我们的角度,我们是怎么想的。

我们始终相信,良好的编程语言是项目成功的基石。诚然,任何语言和框架都只是工具,优秀的程序员不论用什么工具都能把项目做好。不过良好的语言更能降低项目整体风险,让开发人员心情愉悦,提升协作效率,从长远来看更能降低整个团队的研发成本!

我们始终相信,Cocos Creator 不仅仅是一个小游戏引擎,它必定要能支撑大型重度游戏的开发。JavaScript 或许能满足高手自己做一些小项目的需要,或许能快速做点小东西玩玩,但是达不到大型项目的工业标准。

我们始终相信,JavaScript 做为弱类型语言,迟早会成为 Cocos Creator 项目的优化瓶颈,只有强类型语言才有机会彻底提升整个项目的性能表现。所以我们持续在探索 JavaScript 编译为原生语言的可能性,这离不开 TypeScript 的支持。今天已经有 AssemblyScript 这样成熟的项目支持将 TypeScript 方言编译为 WebAssembly,事实上同类项目还有很多。只有从生态、社区的角度统一大家的开发语言,将来我们才有机会为大家送上这份大礼。

我们始终相信,语言的割裂会带来生态的割裂。今天在 Cocos Creator 社区已经拥有了一帮 TypeScript 的簇拥,很多优质的教程、插件、帖子都使用 TypeScript 发布。如果官方文档、官方案例都使用一门语言编写,将不利于另一门语言的学习。如果社区长期拥有两门语言,更不利于大家复用前人的工作成果。JavaScript 开发者无法适应 TypeScript 项目的维护,TypeScript 开发者在复用原有的 JavaScript 组件时也会遇到阻碍。

以下内容由我们的引擎质监局长收集自论坛反馈

常见的 TypeScript 认知误区 误区:Cocos Creator 仅支持 TypeScript,不支持 JavaScript

TypeScript 是 JavaScript 的超集并且 TypeScript 紧紧依赖 JavaScript。Cocos Creator 3.0 仍支持 TypeScript 和 JavaScript 并用。

然而,Cocos Creator 鼓励用户使用 TypeScript 以获得更好的开发体验,提高开发质量,因此在编辑器中 仅支持创建 TypeScript 脚本。

如果你确定一定要使用 JavaScript,以其他方式(资源管理器、访达等)创建 JavaScript 文件仍然是允许的。

误区:import/export 是 TypeScript 专有的,我无法在 JavaScript 中使用 require

import/export 是 JavaScript 规范 ECMAScript 2015 引入的对模块支持的语法,因此 JavaScript 中是可以使用的,并不是 TypeScript 专有。

另一方面,require/exports/module 是 CommonJS 模块系统中的变量。它们不属于 JavaScript 标准。

尽管,在 3.0 中提供了对 CommonJS 模块的有限支持。

将 JavaScript 代码迁移为 TypeScript

上述有提到 JavaScript 可以直接使用。但只要了解 TypeScript 的用法,将 JavaScript 代码迁移为 TypeScript 也是一件轻松的事。

最简单的迁移:改后缀名

可以直接将 .js 文件重命名为 .ts 完成最简单的迁移——只要 JavaScript 在运行时没问题,那么如此改为 TypeScript 也一定是没问题的。

改名后,在 IDE 中会有一大堆报错,这是因为缺少类型信息。如果你暂时不想解决这些类型问题,大可以在整个文件的头部加一句注释:

// @ts-nocheck

来跳过对该文件的类型检查。

在代码中补充类型信息

对于比较简单的 JavaScript 代码,只要稍加补充一些类型信息就可以。例如:

function fn(a, b, c) { // ... }

为 fn 指定参数类型就 OK:

function fn(a: string, b: number, c: boolean) { // ... }

TypeScript 会自动根据函数体内的代码推断出函数的返回值。你当然也可以显式指定:

function fn(a: string, b: number, c: boolean): string { // ... } 在代码外部声明类型信息

有些时候,JavaScript 代码不是我们自己写的,而是由第三方提供的库代码。这时候我们可以在不编辑它的情况下为它补充类型信息。

例如,有一个第三方的 JavaScript 文件 foo.js:

module.exports = function foo (a, b, c) { // ... }

我们可以在同目录下创建一个同名但扩展名为 .d.ts 的 foo.d.ts:

// 声明 foo 的签名即可,函数体不需要 function foo(a: string, b: number, c: boolean): string; export default foo; 严格模式:“null” 问题

从 JavaScript 转入 TypeScript 的同学可能被一些“类型问题”所困扰。

看这样一个问题:

class C { material: Material = null; }

这段代码在 IDE 中会报错,报错源头是属性 material 的声明。

有一种情况是,material 属性仅在初始化时是空值,但是后续任何时候访问都是有值的。例如,给该属性附加 @property 装饰器时,就可以在编辑器中编辑该字段,拖拖拉拉之后由 Creator 帮我们赋值该字段。

那我们如何向 TypeScript 传达这种信息呢?

我们来分析一下报错的原因,material: Material 将 material 字段声明为 Material 类型,这个意思就是在任何时候拿到 material 它都是 Material 类型。

初始化式 = null 告诉它将 material 初始化为 空值 null,与上条说法违背。

这便是,TypeScript 的严格类型检查。它要求你将类型对上号。TypeScript 编辑器是默认开启该选项的,Creator 也不例外。

既然知道了出错原因,那么我们就可以有以下几种思路去解决。

正确描述它的类型为:既可能是 Material,也可能是 null 向 TypeScript 类型系统表达:我只是初始化为 null,后续使用时候其实都是 Material 关闭 TypeScript 对空值的检查。 可空类型

我们可以将 material 的类型描述为既可能是 Material,也可能是 null:

material: Material | null = null;

这样初始化为 null 就顺理成章了。

然而,这样的缺陷是当你后续访问 material 时,TypeScript 要求你处理空值的情况。例如:

console.log(this.material.name);

TypeScript 类型系统会提示你:this.material 可能是空值 null,无法访问 null 的属性。

但是,只要 TypeScript 在此处知道它一定是不为空的,它就会收起这条错误:

if (this.material) { console.log(this.material.name); }

因为你做了判断,当运行到 if 语句块内的时候,this.material 一定是不为空的,因此可以访问 name 属性。

表达式非空断言

每次使用的时候都要用 if 语句判断一下实在有些繁琐,况且 if 语句在运行时是会去执行的。

如果你一定能确保“在运行到这里的时候 this.material 一定非空”,那么我们可以用TypeScript 非空断言语法来表达:

console.log(this.material!.name);

感叹号 ! 称作 非空断言操作符,它断言前面的表达式是非空的。

注意此感叹号是 TypeScript 特有的,仅为类型目的;编译后,会直接移除。

我们还可在初始化时就作此断言:

material: Material = null!; // 相当于将 `null` 强制转换为了 `Material` 类型

还一个经常用于非空断言的地方是 Node.getComponent(),此方法返回的是可能为空的组件。如果能确保组件一定存在,则可以通过非空断言来避免 if 判断。

显式赋值断言

在标准的 JavaScript 语法里,是可以不给初始化式的,这样的字段将被初始化为 undefined:

class C { @property(Material) material; // undefined }

在 TypeScript 里也允许这么做,不过你得提示一下 TypeScript,以让它跳过这里的类型检查:

material!: Material;

属性名后的感叹号称为 显式赋值断言(Definite Assignment Assertion),它告诉 TypeScript 此字段在别处初始化。

OK,这里你将得到一个虽然声明为 Material,但是初始化为 undefined 的字段。

禁用空值检查

当然了,如果你是非常不喜欢 TypeScript 的类型系统,你更喜欢所有事靠自己确保,那么你可以关闭严格类型检测。在 /tsconfig.json 里,加上选项:

"extends": "./temp/tsconfig.cocos.json", "compilerOptions": { "strictNullChecks": false // 关闭它 }

需要提醒的是,我们并不鼓励这种做法,因为严格空值检查能够减少 JS 代码运行时的一些低级报错。放一张最近我们收到的用户反馈做为旁证:

Pasted_Image_2021_02_25__15_00Pasted_Image_2021_02_25__15_00383×590 37.6 KB 总结

以上就是此次跟大家介绍的全部内容。感谢大家在论坛里将工作中遇到的问题如实反馈,才让我们有机会整理成文。

如果大家仍有 TypeScript 使用上的问题,可以在本帖回复,也欢迎大家将本文转发给有需要的人!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有